package com.xuecheng.media.jobhandler;

import com.xuecheng.base.execption.XueChengPlusException;
import com.xuecheng.base.model.RestResponse;
import com.xuecheng.base.utils.Mp4VideoUtil;
import com.xuecheng.media.model.po.MediaProcess;
import com.xuecheng.media.service.MediaFileProcessService;
import com.xuecheng.media.service.MediaFileService;
import com.xxl.job.core.context.XxlJobHelper;
import com.xxl.job.core.handler.annotation.XxlJob;
import io.minio.GetObjectArgs;
import io.minio.MinioClient;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.*;
import java.util.List;
import java.util.concurrent.*;

@Slf4j
@Component
public class VideoTask {
    @Autowired
    MediaFileService mediaFileService;
    @Autowired
    MediaFileProcessService mediaFileProcessService;
    @Autowired
    MinioClient minioClient;


    @Value("${videoprocess.ffmpegpath}")
    String ffmpegpath;

    @XxlJob("videoJobHandler")
    public void shardingJobHandler() throws Exception {
        // 分片参数
        int shardIndex = XxlJobHelper.getShardIndex();
        int shardTotal = XxlJobHelper.getShardTotal();

        List<MediaProcess> mediaProcessList = null;

        int size = 0;

        try {
            //取出cpu核心数作为一次处理数据的条数
            int processors = Runtime.getRuntime().availableProcessors();

            //这些就是从数据库表mediaProcess取出来待处理的视频，并且失败次数小与3
            mediaProcessList = mediaFileProcessService.getMediaProcessList(shardIndex, shardTotal, processors);

            size = mediaProcessList.size();

            log.debug("取出待处理视频任务{}条", size);

            if (size == 0) {
                return;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return;
        }

        //启动size个线程的线程池
        ExecutorService threadPool = Executors.newFixedThreadPool(size);
        //计数器
        CountDownLatch countDownLatch = new CountDownLatch(size);

        //将处理任务加入线程池
        for (MediaProcess mediaProcess : mediaProcessList) {
            threadPool.execute(() -> {
                //任务id
                Long taskId = mediaProcess.getId();
                //抢占任务,也就是把status改为4，正在进行中
                boolean b = mediaFileProcessService.startTask(taskId);
                if (!b) {
                    return;
                }
                log.debug("开始执行任务:{}", mediaProcess);
                //下边是处理逻辑
                //桶
                String bucket = mediaProcess.getBucket();
                //原视频md5
                String fileId = mediaProcess.getFileId();
                //存储路径
                String filePath = mediaProcess.getFilePath();
                //原始文件名称
                String filename = mediaProcess.getFilename();
                //下载上传完后的文件
                File originalFile = downloadFileFromMinIO(bucket, filePath);
                if (originalFile == null) {
                    log.debug("下载待处理文件失败，originalFile:{}", bucket.concat(filePath));
                    mediaFileProcessService.saveProcessFinishStatus(mediaProcess.getId(), "3", fileId, null, "下载待处理文件失败");
                    return;
                }
                File mp4File = null;
                try {
                    mp4File = File.createTempFile("mp4", ".mp4");
                } catch (IOException e) {
                    log.error("创建mp4临时文件失败");
                    mediaFileProcessService.saveProcessFinishStatus(mediaProcess.getId(), "3", fileId, null, "创建mp4临时文件失败");
                    return;
                }
                //视频处理结果
                String result = "";
                //原视频信息还有转换后视频信息还有ffmpeg都整合在一起
                try {
                    Mp4VideoUtil videoUtil = new Mp4VideoUtil(ffmpegpath, originalFile.getAbsolutePath(), mp4File.getName(), mp4File.getAbsolutePath());

                    result= videoUtil.generateMp4();
                } catch (Exception e) {
                    e.printStackTrace();
                    log.error("处理视频文件：{},出错:{}",mediaProcess.getFilePath(),e.getMessage());
                }
                if (!result.equals("success")){
                    //记录错误信息
                    log.error("处理视频失败，视频地址:{}，错误信息:{}",bucket+filePath,result);
                    mediaFileProcessService.saveProcessFinishStatus(mediaProcess.getId(),"3",fileId,null,result);
                    return;
                }
                //将mp4上传至minio
                //mp4在minio的存储路径
                String objectName = mediaFileService.getFilePathByMd5(fileId, ".mp4");
                //做出url
                String url="/"+bucket+"/"+objectName;
                try {
                    mediaFileService.addMediaFilesToMinIO(mp4File.getAbsolutePath(),"video/mp4",bucket,objectName);
                    //将url存储至数据，并更新状态为成功，并将待处理视频记录删除存入历史
                    mediaFileProcessService.saveProcessFinishStatus(mediaProcess.getId(),"2",fileId,url,null);
                } catch (Exception e) {
                    log.error("上传视频失败或入库失败,视频地址:{},错误信息:{}", bucket + objectName, e.getMessage());
                    mediaFileProcessService.saveProcessFinishStatus(mediaProcess.getId(), "3", fileId, null, "处理后视频上传或入库失败");
                }finally {
                    countDownLatch.countDown();
                }
            });
            //等待，给一个充裕的超时时间，防止无限等待，到达时间还没有处理完成就结束任务
            countDownLatch.await(30,TimeUnit.MINUTES);
        }
    }

    private File downloadFileFromMinIO(String bucket, String filePath) {
        //下载上传完后的文件
        InputStream stream = null;
        File tempFile = null;
        try {
            stream = minioClient.getObject(
                    GetObjectArgs.builder()
                            .bucket(bucket)
                            .object(filePath)
                            .build()
            );
            tempFile = File.createTempFile("minio", ".merge");
            OutputStream ops = new FileOutputStream(tempFile);
            IOUtils.copy(stream, ops);
            ops.close();
            return tempFile;
        } catch (Exception e) {
            throw new XueChengPlusException("下载视频失败");
        }
    }


}
